﻿using System;
using System.Windows;
using System.Linq;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Markup;
using System.Windows.Media;
using MSPToolkit.HTMLTools;
using System.Windows.Media.Imaging;
using System.Xml.Linq;
using System.IO;


namespace MSPToolkit.Controls
{

    [ContentPropertyAttribute("Html")]
    public class HTMLTextBox : RichTextBox 
    {

        public HTMLTextBox():base()
        {

        }

        [System.ComponentModel.Category("Common")]
        [System.ComponentModel.DefaultValue("")]
        public string Html
        {
            get 
            {
                return (string)GetValue(HtmlProperty); 
            }
            set 
            {
                SetValue(HtmlProperty, value); 
            }
        }

        // Using a DependencyProperty as the backing store for HTML.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty HtmlProperty =
            DependencyProperty.Register("Html", typeof(string), typeof(HTMLTextBox), new PropertyMetadata("", (d, e) => {
                HTMLTextBox tb = d as HTMLTextBox;
                tb.OnHtmlPropertyChanged(e);
            }));

        private void OnHtmlPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            System.Threading.ThreadPool.QueueUserWorkItem((x) =>
            {
                lock (this)
                {
                    if (string.IsNullOrWhiteSpace((string)e.NewValue))
                    {
                        Dispatcher.BeginInvoke(() =>
                        {
                            Blocks.Clear();
                        });
                        return;
                    }
                    var xamlXml = HtmlToXamlConverter.ConvertHtmlToXamlInternal((string)e.NewValue, false);
                    foreach (var hyperlink in xamlXml.Descendants(XName.Get(HtmlToXamlConverter.Xaml_Hyperlink, HtmlToXamlConverter._xamlNamespace)))
                    {
                        if (hyperlink.Attribute(HtmlToXamlConverter.Xaml_Foreground) == null)
                        {
                            hyperlink.SetAttributeValue(XName.Get(HtmlToXamlConverter.Xaml_Foreground, HtmlToXamlConverter._xamlNamespace), @"Transparent");
                        }
                    }
                    var sections = xamlXml.Descendants(XName.Get(HtmlToXamlConverter.Xaml_Section, HtmlToXamlConverter._xamlNamespace)).ToArray();
                    foreach (var section in sections)
                    {
                        section.AddAfterSelf(section.Nodes());
                        section.Remove();
                    }
                    Dispatcher.BeginInvoke(() =>
                    {
                        var xamlString = xamlXml.ToString();
                        var section = System.Windows.Markup.XamlReader.Load(xamlString) as System.Windows.Documents.Section;
                        Blocks.Clear();
                        GetOutBlocks(section);
                    });
                }
            });
        }

        private void GetOutBlocks(Section section)
        {
            var count = section.Blocks.Count;

            Block[] array = new Block[count];
            section.Blocks.CopyTo(array, 0);
            section.Blocks.Clear();
            foreach (var block in array)
            {
                if (block.GetType() == typeof(Section))
                {
                    GetOutBlocks(block as Section);
                    continue;
                }
                Blocks.Add(block);

                var p = block as Paragraph;
                if (p != null)
                {
                    postParseInlinesSettings(p.Inlines);
                }

            }
        }

        void postParseInlinesSettings(InlineCollection collection)
        {
            foreach (var il in collection)
            {
                var h = il as Hyperlink;
                if (h != null)
                {

                    if (h.Foreground.GetType() == typeof(SolidColorBrush) && ((SolidColorBrush)h.Foreground).Color == Colors.Transparent)
                    {
                        h.Foreground = this.Foreground;
                    }

                    h.CommandParameter = h.NavigateUri;
                    if (NavigaionPolitic == HTMLTextBoxHyperlynkNavigaionPolitic.Automatic)
                    {
                        h.TargetName = "_blank";
                    }
                    else
                    {
                        h.NavigateUri = null;
                    }
                    h.Click += new RoutedEventHandler(hyperlink_Click);
                    postParseInlinesSettings(h.Inlines);
                    continue;
                }
                var ui = il as InlineUIContainer;
                if (ui != null)
                {
                    (ui.Child as Button).Template = null;
                    (ui.Child as Button).Click += new RoutedEventHandler(HTMLTextBox_Click);
                    var im = (ui.Child as Button).Content as Image;
                    im.ImageOpened += new EventHandler<RoutedEventArgs>(HTMLTextBox_ImageOpened);
                    var source = im.Source as BitmapImage;
                    // support for base64 encoded images
                    if (source == null && !string.IsNullOrEmpty(im.Name))
                    {
                        var base64string = im.Name;
                        byte[] fileBytes = Convert.FromBase64String(base64string);
                        source = new BitmapImage();
                        using (MemoryStream ms = new MemoryStream(fileBytes, 0, fileBytes.Length))
                        {
                            ms.Write(fileBytes, 0, fileBytes.Length);
                            source.CreateOptions = BitmapCreateOptions.None;
                            source.SetSource(ms);
                        }
                        im.Source = source;
                    }
                    if (source.PixelWidth > 0 && ActualWidth > 24 && double.IsNaN(im.Width))
                    {
                        var width = (double)ActualWidth - 24;
                        if (source.PixelWidth < width)
                            width = source.PixelWidth;
                        if (im.Width < width && im.Width != 0)
                            width = im.Width;

                        im.Width = width;
                        im.Height = source.PixelHeight * width / source.PixelWidth;
                    }

                    continue;
                }
                var span = il as Span;
                if (span != null)
                {
                    postParseInlinesSettings(span.Inlines);
                }
            }
        }

        void HTMLTextBox_Click(object sender, RoutedEventArgs e)
        {
            if (ImageClick != null)
            {
                var im = (sender as Button).Content as Image;
                ImageClick(im, new ImageClickEventArgs(im.Source));
            }
        }

        void HTMLTextBox_ImageOpened(object sender, RoutedEventArgs e)
        {
            var im = sender as Image;
            var source = im.Source as BitmapImage;
            if (source.PixelWidth > 0 && ActualWidth > 24 && double.IsNaN(im.Width))
            {
                var width = (double)ActualWidth - 24;
                if (source.PixelWidth < width)
                    width = source.PixelWidth;
                if (im.Width < width && im.Width != 0)
                    width = im.Width;

                im.Width = width;
                im.Height = source.PixelHeight * width / source.PixelWidth;
            }
        }

        void hyperlink_Click(object sender, RoutedEventArgs e)
        {
            
            if (HyperlinkClick != null)
            {
                HyperlinkClick(sender, new HyperlinkClickEventArgs((sender as Hyperlink).CommandParameter as Uri));
            }
        }

        /// <summary>
        /// Occurs when the left mouse button is clicked on a some Hyperlink in text.
        /// </summary>
        public event EventHandler<HyperlinkClickEventArgs> HyperlinkClick;


        /// <summary>
        /// Occurs when the left mouse button is clicked on a some Image.
        /// </summary>
        public event EventHandler<ImageClickEventArgs> ImageClick;

        /// <summary>
        /// Determine how links navigates. Auto value will open WebBrowser if it can. Both Auto and Manual will rise HyperlinkClick event.
        /// </summary>
        public HTMLTextBoxHyperlynkNavigaionPolitic NavigaionPolitic { get; set; }

    }

}
